﻿/**
 * Hardware.cs - To hide SDL basic capabilities
 * 
 * Part of BoulderDAM - A BoulderDash clone using C# and SDL
 * Nacho Cabanes & students of DAM IES San Vicente, 2014
 * 
  * Revisions
 * #     Date         Creator, changes
 * ----  -----------  -------------------------------------------------------
 * 0.01  02-apr-2014  Nacho Cabanes: initialization, displaying images,
 *                        displaying text, keyboard access, 
 *                        basic scrolling support
 * 0.02, 03-apr-2014  Javier Soler, Reinaldo Ferreira, Luis Massó:  
 *                        Mouse Methods & Joystick Methods
 * 0.03  04-apr-2014  Nacho Cabanes: Skeleton for PopUp support
 * 0.04  07-apr-2014  Adil Casamayor, Ramon March, Jose Valdes.  Implemented ShowPopUp and RectangleRGBA
 */

using System.IO;
using System.Threading;
using Tao.Sdl;
using System;

namespace Boulder
{
    class Hardware
    {
        static IntPtr hiddenScreen;
        static short width, height;

        static short startX, startY; // For basic Scrolling support

        //Joystick and Mouse declarations
        static bool isThereJoystick;
        static IntPtr joystick;

        static int mouseClickLapse;
        static int lastMouseClick;

        public static void Init(short w, short h, int colors, bool fullScreen)
        {
            width = w;
            height = h;

            int flags = Sdl.SDL_HWSURFACE | Sdl.SDL_DOUBLEBUF | Sdl.SDL_ANYFORMAT;
            if (fullScreen)
                flags |= Sdl.SDL_FULLSCREEN;
            Sdl.SDL_Init(Sdl.SDL_INIT_EVERYTHING);
            hiddenScreen = Sdl.SDL_SetVideoMode(
              width,
              height,
              colors,
              flags);

            Sdl.SDL_Rect rect2 =
              new Sdl.SDL_Rect(0, 0, (short)width, (short)height);
            Sdl.SDL_SetClipRect(hiddenScreen, ref rect2);

            SdlTtf.TTF_Init();

            // Joystick initialization
            isThereJoystick = true;
            if (Sdl.SDL_NumJoysticks() < 1)
                isThereJoystick = false;

            if (isThereJoystick)
            {
                joystick = Sdl.SDL_JoystickOpen(0);
                if (joystick == IntPtr.Zero)
                    isThereJoystick = false;
            }

            // Time lapse between two consecutive mouse clicks,
            // so that they are not too near
            mouseClickLapse = 10;
            lastMouseClick = Sdl.SDL_GetTicks();
        }

        public static void ClearScreen()
        {
            Sdl.SDL_Rect origin = new Sdl.SDL_Rect(0, 0, width, height);
            Sdl.SDL_FillRect(hiddenScreen, ref origin, 0);
        }

        public static void DrawHiddenImage(Image image, int x, int y)
        {
            drawHiddenImage(image.GetPointer(), x + startX, y + startY);
        }

        public static void RectangleRGBA(short x1, short y1, short x2, short y2,
            byte r, byte g, byte b, byte a)
        {
            SdlGfx.rectangleRGBA(hiddenScreen,
              x1, y1, x2, y2, r, g, b, a);
        }

        public static void ShowHiddenScreen()
        {
            Sdl.SDL_Flip(hiddenScreen);
        }

        public static bool KeyPressed(int c)
        {
            bool pressed = false;
            Sdl.SDL_PumpEvents();
            Sdl.SDL_Event myEvent;
            Sdl.SDL_PollEvent(out myEvent);
            int numkeys;
            byte[] keys = Tao.Sdl.Sdl.SDL_GetKeyState(out numkeys);
            if (keys[c] == 1)
                pressed = true;
            return pressed;
        }

        public static void Pause(int milisegundos)
        {
            Thread.Sleep(milisegundos);
        }

        public static int GetWidth()
        {
            return width;
        }

        public static int GetHeight()
        {
            return height;
        }

        public static void FatalError(string text)
        {
            StreamWriter sw = File.AppendText("errors.log");
            sw.WriteLine(text);
            sw.Close();
            Console.WriteLine(text);
            Environment.Exit(1);
        }

        public static void WriteHiddenText(string txt,
          short x, short y, byte r, byte g, byte b, Font f)
        {
            if (txt.Length == 0) return;

            Sdl.SDL_Color color = new Sdl.SDL_Color(r, g, b);
            IntPtr textoComoImagen = SdlTtf.TTF_RenderText_Solid(
              f.GetPointer(), txt, color);
            if (textoComoImagen == IntPtr.Zero)
                Environment.Exit(5);

            Sdl.SDL_Rect origen = new Sdl.SDL_Rect(0, 0, width, height);
            Sdl.SDL_Rect dest = new Sdl.SDL_Rect(
                (short)(x + startX), (short)(y + startY),
                width, height);

            Sdl.SDL_BlitSurface(textoComoImagen, ref origen,
              hiddenScreen, ref dest);
        }


        // Scroll Methods

        public static void ResetScroll()
        {
            startX = startY = 0;
        }

        public static void ScrollTo(short newStartX, short newStartY)
        {
            startX = newStartX;
            startY = newStartY;
        }

        public static void ScrollHorizontally(short xDespl)
        {
            startX += xDespl;
        }

        public static void ScrollVertically(short yDespl)
        {
            startY += yDespl;
        }

        // Joystick methods

        /** JoystickPressed: returns TRUE if
         *  a certain button in the joystick/gamepad
         *  has been pressed
         */
        public static bool JoystickPressed(int boton)
        {
            if (!isThereJoystick)
                return false;

            if (Sdl.SDL_JoystickGetButton(joystick, boton) > 0)
                return true;
            else
                return false;
        }

        /** JoystickMoved: returns TRUE if
         *  the joystick/gamepad has been moved
         *  up to the limit in any direction
         *  Then, int returns the corresponding
         *  X (1=right, -1=left)
         *  and Y (1=down, -1=up)
         */
        public static bool JoystickMoved(out int posX, out int posY)
        {
            posX = 0; posY = 0;
            if (!isThereJoystick)
                return false;

            posX = Sdl.SDL_JoystickGetAxis(joystick, 0);  // Read values (until 32768) full move
            posY = Sdl.SDL_JoystickGetAxis(joystick, 1);
            // Values normalization
            if (posX == -32768) posX = -1;  // Set values between -1, +1 or 0
            else if (posX == 32767) posX = 1;
            else posX = 0;
            if (posY == -32768) posY = -1;
            else if (posY == 32767) posY = 1;
            else posY = 0;

            if ((posX != 0) || (posY != 0))
                return true;
            else
                return false;
        }

        /** JoystickMovedRight: returns TRUE if
         *  the joystick/gamepad has been moved
         *  completely to the right
         */
        public static bool JoystickMovedRight()
        {
            if (!isThereJoystick)
                return false;

            int posX = 0, posY = 0;
            if (JoystickMoved(out posX, out posY) && (posX == 1))
                return true;
            else
                return false;
        }

        /** JoystickMovedLeft: returns TRUE if
         *  the joystick/gamepad has been moved
         *  completely to the left
         */
        public static bool JoystickMovedLeft()
        {
            if (!isThereJoystick)
                return false;

            int posX = 0, posY = 0;
            if (JoystickMoved(out posX, out posY) && (posX == -1))
                return true;
            else
                return false;
        }


        /** JoystickMovedUp: returns TRUE if
         *  the joystick/gamepad has been moved
         *  completely upwards
         */
        public static bool JoystickMovedUp()
        {
            if (!isThereJoystick)
                return false;

            int posX = 0, posY = 0;
            if (JoystickMoved(out posX, out posY) && (posY == -1))
                return true;
            else
                return false;
        }


        /** JoystickMovedDown: returns TRUE if
         *  the joystick/gamepad has been moved
         *  completely downwards
         */
        public static bool JoystickMovedDown()
        {
            if (!isThereJoystick)
                return false;

            int posX = 0, posY = 0;
            if (JoystickMoved(out posX, out posY) && (posY == 1))
                return true;
            else
                return false;
        }

        // Mouse methods

        /** GetMouseX: returns the current X
         *  coordinate of the mouse position
         */
        public static int GetMouseX()
        {
            int posX = 0, posY = 0;
            Sdl.SDL_PumpEvents();
            Sdl.SDL_GetMouseState(out posX, out posY);
            return posX;
        }


        /** GetMouseY: returns the current Y
         *  coordinate of the mouse position
         */
        public static int GetMouseY()
        {
            int posX = 0, posY = 0;
            Sdl.SDL_PumpEvents();
            Sdl.SDL_GetMouseState(out posX, out posY);
            return posY;
        }

        /** MouseClicked: return TRUE if
          *  the (left) mouse button has been clicked
          */
        public static bool MouseClicked()
        {
            int posX = 0, posY = 0;

            Sdl.SDL_PumpEvents();

            // To avoid two consecutive clicks
            int now = Sdl.SDL_GetTicks();
            if (now - lastMouseClick < mouseClickLapse)
                return false;

            // Check if click exists
            if ((Sdl.SDL_GetMouseState(out posX, out posY) & Sdl.SDL_BUTTON(1)) == 1)
            {
                lastMouseClick = now;
                return true;
            }
            else
                return false;
        }



        public static int POPUP_OK = 1;
        public static int POPUP_CANCEL = 0;

        // Displays a certain text, centered on screen, waits for an okKey
        // or a cancelKey and returns POPUP_CANCEL or POPUP_OK
        public static int ShowPopUp(string text, int cancelKey, int okKey)
        {/*
            int indent = 20;
            int initX = (1024 / 2) - ((text.Length / 2) + indent);
            int initY = 768 / 2 - indent;*/
            RectangleRGBA(10, 10, 1024 - 10, 768 - 10, 255, 255, 255, 128);
            ShowHiddenScreen();
            Hardware.Pause(2000);
            // TO DO
            return POPUP_OK;
        }

        // Private (auxiliar) methods

        private static void drawHiddenImage(IntPtr image, int x, int y)
        {
            Sdl.SDL_Rect origin = new Sdl.SDL_Rect(0, 0, width, height);
            Sdl.SDL_Rect dest = new Sdl.SDL_Rect((short)x, (short)y,
              width, height);
            Sdl.SDL_BlitSurface(image, ref origin, hiddenScreen, ref dest);
        }


        // Alternate key definitions

        public static int KEY_ESC = Sdl.SDLK_ESCAPE;
        public static int KEY_SPC = Sdl.SDLK_SPACE;
        public static int KEY_A = Sdl.SDLK_a;
        public static int KEY_B = Sdl.SDLK_b;
        public static int KEY_C = Sdl.SDLK_c;
        public static int KEY_D = Sdl.SDLK_d;
        public static int KEY_E = Sdl.SDLK_e;
        public static int KEY_F = Sdl.SDLK_f;
        public static int KEY_G = Sdl.SDLK_g;
        public static int KEY_H = Sdl.SDLK_h;
        public static int KEY_I = Sdl.SDLK_i;
        public static int KEY_J = Sdl.SDLK_j;
        public static int KEY_K = Sdl.SDLK_k;
        public static int KEY_L = Sdl.SDLK_l;
        public static int KEY_M = Sdl.SDLK_m;
        public static int KEY_N = Sdl.SDLK_n;
        public static int KEY_O = Sdl.SDLK_o;
        public static int KEY_P = Sdl.SDLK_p;
        public static int KEY_Q = Sdl.SDLK_q;
        public static int KEY_R = Sdl.SDLK_r;
        public static int KEY_S = Sdl.SDLK_s;
        public static int KEY_T = Sdl.SDLK_t;
        public static int KEY_U = Sdl.SDLK_u;
        public static int KEY_V = Sdl.SDLK_v;
        public static int KEY_W = Sdl.SDLK_w;
        public static int KEY_X = Sdl.SDLK_x;
        public static int KEY_Y = Sdl.SDLK_y;
        public static int KEY_Z = Sdl.SDLK_z;
        public static int KEY_1 = Sdl.SDLK_1;
        public static int KEY_2 = Sdl.SDLK_2;
        public static int KEY_3 = Sdl.SDLK_3;
        public static int KEY_4 = Sdl.SDLK_4;
        public static int KEY_5 = Sdl.SDLK_5;
        public static int KEY_6 = Sdl.SDLK_6;
        public static int KEY_7 = Sdl.SDLK_7;
        public static int KEY_8 = Sdl.SDLK_8;
        public static int KEY_9 = Sdl.SDLK_9;
        public static int KEY_0 = Sdl.SDLK_0;
        public static int KEY_UP = Sdl.SDLK_UP;
        public static int KEY_DOWN = Sdl.SDLK_DOWN;
        public static int KEY_RIGHT = Sdl.SDLK_RIGHT;
        public static int KEY_LEFT = Sdl.SDLK_LEFT;
        public static int KEY_RETURN = Sdl.SDLK_RETURN;
    }
}
